#include "IntroDistance.fx"


uniform float3 gCamPos;
uniform float3 gViewDir;
uniform float3 gRightToEnd;
uniform float3 gUpToEnd;
uniform float3 gLightPos;

struct VS_IN
{
	float3 posL     : POSITION;
	float2 texcoord : TEXCOORD0;
};

struct VS_OUT
{
	float4 posH     : SV_POSITION;
	float2 texcoord : TEXCOORD0;
};
 
VS_OUT VS(VS_IN vIn)
{
	VS_OUT vOut;
	vOut.posH = float4(vIn.posL, 1.0f);
	vOut.texcoord = vIn.texcoord;
	return vOut;
}








#define MAX_ITER_VISUALIZE 64.0f

float3 numIterVis(float numIter)
{
	if(numIter == 2)
		return float3(1, 1, 1);

	float QUARTER = MAX_ITER_VISUALIZE * 0.25f;
	float HALF = MAX_ITER_VISUALIZE * 0.5f;
	
	float B = lerp(0.5f, 0.0f, numIter / QUARTER);
	float G ;//= lerp(1.0f, 0.0f, abs(numIter - QUARTER) / QUARTER);
	if(numIter < HALF)
		G = lerp(0.0f, 1.0f, numIter / QUARTER);
	else
		G = lerp(1.0f, 0.0f, (numIter - HALF) / HALF);
	float R = lerp(0.0f, 1.0f, (numIter - QUARTER) / QUARTER);
	//float a = numIter / 50.0f;
	return float3(R, G, B);
}

/*
float3 lighting(float3 position, float3 N, float3 V)
{
	float3 diffuse = float3(0.2f, 0.2f, 0.2f);

	// lightPos = float3(0.0f, 2.0f, 1.0f);
	float3 lightCol = float3(1.0f, 1.0f, 0.8f);

	//float3 L = normalize(gLightPos - position);
	float L = normalize(float3(1,-1,1));
	diffuse += lightCol * saturate(dot(N, L));

	return diffuse;
}
*/

float calcAO(float3 P, float3 N, float k, float it)
{
	float ao = 1.0f;
	int matID;
	for(float i = 0.0f ; i < it ; i += 1.0f)
	{
		ao -= (i*abs(k) - distFunc(P + i*N*k, matID)) / pow(2.0f, i);
	}
	return ao;
}

float calcShadow2(float3 P, float3 L)
{
	float ao = 1.0f;
	const float k = 1.3f;
	int matID;
	for(float i = 0.0f ; i < 5.0f ; i += 1.0f)
	{
		ao -= (i*k - distFunc(P + i*L*k, matID)) / pow(2.0f, i*0.2f) / 10;
	}
	return ao;
}

float calcShadow(float3 P, float3 L)
{
	const float shadowOffset = 1.0f + EPS;

	float3 currPos = P + L * shadowOffset;
	float minDist = 99999.0f;
	float rayAdvance = shadowOffset;

	while(distance(currPos, P) < 100.0f)
	{
		int matID;
		float dist = distFunc(currPos, matID);
		if(dist < EPS)
		{
			return 0.0f;
		}
		minDist = min(dist, minDist);
		currPos += L * 0.95f * dist;
		rayAdvance += 0.95f * dist;
	}
	//if(minDist >= (shadowOffset))
	//	return 1.0f;
	//else
	//if(minDist < 1.0f)
	//	return 0.0f;
	//else return 1.0f;
		return saturate(minDist * 4.0f);
}

/*
float shadow(float3 p, float3 l, float d, float i) {
	float o;
	int matID;
	for (o = 0.; i > 0.; i--) {
		o += distFunc(p+l*i*d, matID) * 0.01f;
	}
	return saturate(o);
}
*/

float3 calcNormal(float3 P)
{
	float3 normal;

	const float EPS_ = 0.03f;
	int matID;
	
	normal.x = distFunc(float3(P.x + EPS_, P.y, P.z), matID) - distFunc(float3(P.x - EPS_, P.y, P.z), matID);
	normal.y = distFunc(float3(P.x, P.y + EPS_, P.z), matID) - distFunc(float3(P.x, P.y - EPS_, P.z), matID);
	normal.z = distFunc(float3(P.x, P.y, P.z + EPS_), matID) - distFunc(float3(P.x, P.y, P.z - EPS_), matID);

	return normalize(normal );
}

float3 getColor(int matID)
{
	float noi = fractalsum(gTexPos / 10) * 0.5f + 0.5f;
	switch(matID)
	{
		case MATERIAL_BOX:
		return float3(0.3f, 0.5f, 1.0f);
		case MATERIAL_PLANE:
		return float3(0.9f, 0.9f, 0.9f);// * float3(noi, noi, noi);
		case MATERIAL_SPHERE:
		return float3(0.7f, 0.3f, 0.5f);
		
	}
}

float4 rayMarching(VS_OUT vIn)
{
	float x = vIn.texcoord.x;
	float y = vIn.texcoord.y;
	
	float3 rayOrig = gCamPos;
	//float3 rayDir = float3(x, y, 2.0f);
	float3 rayDir = gViewDir + gRightToEnd * x + gUpToEnd * y;
	rayDir = normalize(rayDir);


	///////////
	//float time = gTime % 2.0f;
	//time = time - 1.0f;
	//float sphereZ = 8.0f - 15.01f * time * time;
	//float4 sphere1 = float4(sin(gTime) * 3, sphereZ, 25.0f, 1.0f);
	//////////

	
	
	float3 currPos = rayOrig;
	float numIter = 0;

	float3 pixelColor = float3(0.3f, 0.5f, 1.0f);

	//float rot = (currPos + rayDir * length(currPos)).y * gTime / 20;
	
	while(distance(currPos, rayOrig) < 250.0f)
	{
		float rot = currPos.y;

		numIter += 1.0f;

		int matID;
		float dist = distFunc(currPos, matID);
		if(dist < EPS)
		{
			gTexPos = currPos;
			
			float3 normal = calcNormal(currPos);

			float3 diffuse = float3(0.3f, 0.3f, 0.3f);
			float3 lightCol = float3(0.6f, 0.6f, 0.6f);
			//float3 L = normalize(gLightPos - position);
			float3 L = normalize(float3(1,1,1));
			diffuse += lightCol * saturate(dot(normal, L));
			//diffuse += lightCol * distFunc(currPos + 0.1f * L) * 5;
			float specular = pow(saturate(dot(reflect(L, normal), rayDir)), 512);
			
			
			
			float shadowFactor = 1.0f;//calcAO(currPos, rayDir, 0.3f, 5.0f);//calcShadow(currPos, L);
			float aoFactor = calcAO(currPos, normal, 0.2f, 5.0f);
			float sssFactor = 0.0f;//saturate(calcAO(currPos, rayDir, 0.5f, 5.0f) * saturate(dot(rayDir, L)));
			float3 objColor = getColor(matID);
			pixelColor = shadowFactor * ( objColor * aoFactor * diffuse + specular * aoFactor + sssFactor * lightCol * objColor);
			pixelColor += float4(0.3f, 0.1f, 0.1f, 1.0f) / (1.0f + distFunc(float3(1,1,1)*10, matID) * 0.1f);
			//pixelColor = diffuse + sssFactor;
			//pixelColor = shadow(currPos, L, 0.9f, 15) * calcAO(currPos, normal) * getColor(matID) * diffuse + specular;
			break;
		}

		currPos += rayDir * 0.95f * dist;
	}
	
	return float4(pixelColor, 1.0f);
	//return float4(numIterVis(numIter), 1.0f);
}

float4 PS(VS_OUT pIn) : SV_Target
{
	float x = pIn.texcoord.x * 0.5f + 0.5f;
	float y = pIn.texcoord.y * 0.5f + 0.5f;
	
	/*
	return float4(saturate(abs(sin(gTime)) * y * y * y / x),
		saturate(abs(sin(gTime)) * x * x * x / y),
		saturate(abs(sin(gTime))),
		1.0f);
	*/
	
	return rayMarching(pIn);
		
	
	/*	
	return float4
	(
		saturate(x),
		0.0f,
		0.0f,
		1.0f
	);
	*/
}

technique11 Layout
{
    pass P0
    {
        SetVertexShader( CompileShader( vs_5_0, VS() ) );
        SetGeometryShader( NULL );
        SetPixelShader( CompileShader( ps_5_0, PS() ) );
    }
}
